// Created by plusminus on 18:58:15 - 25.09.2008
package org.andnav.osm.views.util;

import org.andnav.osm.views.util.constants.MathConstants;

/**
 * http://wiki.openstreetmap.org/index.php/Mercator
 * 
 * @author Nicolas Gramlich This class provides a way to convert from latitude
 *         and longitude to a simple Mercator projection.
 */
public class Mercator implements MathConstants {

    // ===========================================================
    // Constants
    // ===========================================================

    // private static final double R_MAJOR = 6378137.0;
    // private static final double R_MINOR = 6356752.3142;
    // private static final double RATIO = R_MINOR / R_MAJOR;
    //
    //
    // private static final double ECCENT = Math.sqrt(1.0 - (RATIO * RATIO));
    // private static final double COM = 0.5 * ECCENT;

    // ===========================================================
    // Static Methods
    // ===========================================================

    /**
     * Converts a Mercator-projected y-coordinate (projected latitude) to the
     * real Latitude. This is only a approximation.
     */
    public static double y2lat(final double a) {
        return (180 / Math.PI) * ((2 * Math.atan(Math.exp((a * Math.PI) / 180))) - (Math.PI / 2));
    }

    /**
     * Converts a real Latitude to the Mercator-projected y-coordinate
     * (projected latitude) . This is only a approximation.
     */
    public static double lat2y(final double a) {
        return (180 / Math.PI) * Math.log(Math.tan((Math.PI / 4) + ((a * (Math.PI / 180)) / 2)));
    }

    /**
     * Converts a Mercator-projected x-coordinate (projected longitude) to the
     * real longitude. This is only a approximation.
     */
    public static double x2lon(final double x) {
        return x * MathConstants.RAD2DEG;
    }

    /**
     * Converts a real longitude to the Mercator-projected x-coordinate
     * (projected longitude). This is only a approximation.
     */
    public static double lon2x(final double lon) {
        return MathConstants.DEG2RAD * lon;
    }

    // public static double[] fromMercatorProjection(final int x, final int y){
    // return new double[] {projectedToRealLat(y / 1E6), projectedToRealLon(x /
    // 1E6)};
    // }
    //
    // public static double[] fromMercatorProjection(final double x, final
    // double y){
    // return new double[] {projectedToRealLat(y), projectedToRealLon(x)};
    // }
    //
    // private static double projectedToRealLon(final double x){
    // return x * RAD2DEG / R_MAJOR;
    // }
    //
    // private static double projectedToRealLat(final double y){
    // final double ts = Math.exp(-y / R_MAJOR);
    // double phi = PI_2 - 2 * Math.atan(ts);
    // double dphi = 1.0;
    // int i = 0;
    // while((Math.abs(dphi) > 0.000000001) && (i < 15)) {
    // final double con = ECCENT * Math.sin(phi);
    // dphi = PI_2 - 2 * Math.atan(ts * Math.pow((1.0 - con) / (1.0 + con),
    // COM)) - phi;
    // phi += dphi;
    // i++;
    // }
    // return RAD2DEG * phi;
    // }
    //
    // public static double[] toMercatorProjection(final double aLatitude, final
    // double aLongitude) {
    // return new double[] {realToProjectedLat(aLatitude),
    // realToProjectedLon(aLongitude)};
    // }
    //
    // public static double[] toMercatorProjection(final int aLatitudeE6, final
    // int aLongitudeE6) {
    // return new double[] {realToProjectedLat(aLatitudeE6 / 1E6),
    // realToProjectedLon(aLongitudeE6 / 1E6)};
    // }
    //
    // private static double realToProjectedLon(final double lon) {
    // return R_MAJOR * DEG2RAD * lon;
    // }
    //
    // private static double realToProjectedLat(double lat) {
    // if (lat > 89.5) {
    // lat = 89.5;
    // }
    // if (lat < -89.5) {
    // lat = -89.5;
    // }
    //
    // final double phi = DEG2RAD * lat;
    // final double sinphi = Math.sin(phi);
    // double con = ECCENT * sinphi;
    // con = Math.pow(((1.0 - con) / (1.0 + con)), COM);
    // final double ts = Math.tan(0.5 * (PI_2 - phi)) / con;
    // final double y = 0 - R_MAJOR * Math.log(ts);
    // return y;
    // }
}
